/*!
 * vinculum
 * Copyright(c) 2017 azzly
 * MIT Licensed
 */
 
'use strict';

/**
 * Module dependencies.
 * @private
 */
const vm = require("vm");
 
const helper = require("../helper");
const CONST = require("../CONST");
const config = require("../config");
const debug = require("../debug")(__filename);
const datasource = require("../datasource");
const error = require("../HTTPErrors");
const cache = require('../cache');
const service = require('./service');

const reqireFun = (a)=>{
	if(config.actionContextWhiteList){
		for(var i=0,len=config.actionContextWhiteList.length;i<len;i++){
			if(config.actionContextWhiteList[i] === a){
				return require(a);
			}
		}
	}
	return null;
}
 
/**
 * class.
 */

class action{
  constructor(action_name,request,response){
    this.action_name = action_name;
    this.context = {
      helper:helper
      ,CONST:CONST
      ,config:config
      ,datasource:datasource
      ,debug:require("../debug")(action_name)
      ,request:request
      ,response:response
      ,cache:cache
      ,service:service
			,Buffer:Buffer
			,action:action
      ,require:reqireFun
    };
  }
	
	islocked(info){
    let action_name = this.action_name;
    let locktype = info.locktype;
    if(locktype){
      let cache_name;
      if(locktype === CONST.LOCK_TYPES.ACTION){
        cache_name = helper.md5(action_name);
      }else if(locktype === CONST.LOCK_TYPES.ACTION_PARAMS){
				let params = this.context.params;
				let key = action_name + helper.json2string(params);
        cache_name = helper.md5(key);
      }
			let islocked = cache.getActionLocked(cache_name);
			if(!islocked){
				cache.setActionLocked(cache_name,true);
			}
			return islocked;
    }else{
      return false;
    }
  }
  
  unlocked(info){
    let action_name = this.action_name;
    let locktype = info.locktype;
    if(locktype){
      let cache_name;
      if(locktype === CONST.LOCK_TYPES.ACTION){
        cache_name = helper.md5(action_name);
      }else if(locktype === CONST.LOCK_TYPES.ACTION_PARAMS){
				let params = this.context.params;
				let key = action_name + helper.json2string(params);
        cache_name = helper.md5(key);
      }
			
			cache.setActionLocked(cache_name,false);
    }
  }
  
  excute(params){
    let self = this;
    let action_name = self.action_name;
    self.context.params = params;
    return new Promise((resolve, reject)=>{
      action.find(action_name).then(info=>{
				if(info.disabled === CONST.DISABLED_TYPES.DISABLED){
					reject(error.GONE);
				}else{
					self.context.ret = (err,result)=>{
						if(err){
							reject(err);
							if(err !== error.EXCUTE_ACTION_TOO_FREQUENTLY){
								self.unlocked(info);
							}
						}else{
							resolve(result);
							self.unlocked(info);
						}
					};
					if(self.islocked(info)){
						self.context.ret(error.EXCUTE_ACTION_TOO_FREQUENTLY);
					}else{
						try{
							info.script.runInNewContext(self.context);
						}catch(err){
							self.context.ret(err.message);
						}
					}
				}
      }).catch(err=>{
        reject(err);
      });
    });
  }
  
  static find(action_name){
    return new Promise((resolve, reject)=>{
      if(!action_name) return reject(error.NOT_FOUND_ACTION_NAME);
			
      if(CONST.ACTIONS[action_name]){
        resolve(CONST.ACTIONS[action_name]);
        return;
      }
			
      let info = cache.getAction(action_name);
      if(info){
        resolve(info);
      }else{
        datasource.systemQuery(CONST.SQLS.action_browse,[action_name,action_name]).then(result=>{
          info = result[0];
          if(info){
						info.script = new vm.Script(`try{
							${info.main}
						}catch(err){
							ret(err.message);
						}`);
            cache.setAction(action_name,info);
            resolve(info);
          }else{
            reject(error.NOT_FOUND_ACTION_NAME);
          }
        }).catch(err=>{
          reject(err);
        });
      }
    });
  }
}

module.exports = action;